// Semaphore.cpp
#include "semaphore.hpp"

int CSemaphore::Init(int iMax, int iInit)
{
    int iRet = -1, i;
    int iResult, iTmp;

    if(iMax <= 0 || iInit < 0 || iInit > iMax) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS);
    if(m_hSem) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INITED);

    m_iMax = iMax;
#ifdef WINDOWS
    m_hSem = CreateSemaphoreA(NULL, iInit, m_iMax, NULL);
#endif
#ifdef LINUX
    iResult = sem_init(&m_stSem, 0, iInit);
    if(iResult != 0) GOTO_EXIT;
    m_hSem = &m_stSem;
#endif

    if(m_hSem) iRet = 0;
_Exit:
    if(iRet < 0) Recycle();
    return iRet;
}

int CSemaphore::Send(int iCount)
{
    int iRet = -1;
    int iResult, iTmp;

    if(iCount <= 0 || iCount > m_iMax) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INVALIDARGS);
#ifdef WINDOWS
    iResult = ReleaseSemaphore(m_hSem, iCount, NULL);
    if(iResult) return 0;
    else return COMMON_ERR_NOMEM;
#endif
#ifdef LINUX
    for(int i = 0; i < iCount; i++)
    {
        iResult = sem_post(&m_stSem);
        if(iResult) GOTO_EXIT;
    }
#endif
    iRet = 0;
_Exit:
    return iRet;
}

int CSemaphore::Recv(int iTimeout)
{
    int iRet = -1;
    int iResult, iTmp;

    if(m_hSem == NULL) SET_RET_AND_GOTO_EXIT(COMMON_ERR_UNINITED);

#ifdef WINDOWS
    U32 uTime = INFINITE;

    if(iTimeout >= 0) uTime = iTimeout * 1000;

    iResult = WaitForSingleObject(m_hSem, uTime);
    if(iResult == WAIT_OBJECT_0) iRet = 0;
    else if(iResult == WAIT_TIMEOUT) iRet = COMMON_ERR_TIMEOUT;
#endif
#ifdef LINUX
    struct timespec ts;

    if(iTimeout > 0)
    {
        if(clock_gettime(CLOCK_REALTIME, &ts) == -1) GOTO_EXIT;
        ts.tv_sec += iTimeout;
        iResult = sem_timedwait(&m_stSem, &ts);
    }
    else if(iTimeout == 0) iResult = sem_trywait(&m_stSem);
    else iResult = sem_wait(&m_stSem);

    if(iResult == 0) iRet = 0;
    else if(iResult == -1 && errno == ETIMEDOUT) iRet = COMMON_ERR_TIMEOUT;
#endif
_Exit:
    return iRet;
}

int CSemaphore::Construct()
{
    m_hSem = NULL;
    m_iMax = -1;
    return 0;
}

int CSemaphore::Destruct()
{
#ifdef WINDOWS
    if(m_hSem) CloseHandle(m_hSem);
#endif
#ifdef LINUX
    if(m_hSem) sem_destroy(&m_stSem);
#endif
    return 0;
}